import { Cls } from "./Defined";

/**
 * 对象池对象接口
 */
export interface IPoolObj {

	/** 创建此对象的对象池 */
	pool: Pool<this>;

	/**
	 * 当从对象池中取出时触发
	 * 在此进行初始化操作
	 */
	onAlloc(): any;

	/**
	 * 当回收进对象池里时触发
	 * 在此进行清理操作
	 */
	onFree(): void;
}

/**
 * 对象池
 */
export class Pool<T extends IPoolObj> {

	/** 池里的对象 */
	objs: T[] = [];

	constructor(
		/** 对象的类 */
		readonly cls: Cls<T>,
		/** new对象时传入的参数 */
		public createArgs?: any[],
		/** 最大容量 */
		public max: number = Number.MAX_VALUE
	) { }

	/** 当前池里的对象数量 */
	get count() {
		return this.objs.length;
	}

	/**
	 * 从池里获取一个对象，没有则创建
	 */
	alloc() {
		const obj = this.objs.pop() || (this.createArgs ? new this.cls(...this.createArgs) : new this.cls());
		obj.pool = this;
		obj.onAlloc();
		return obj;
	}

	/**
	 * 回收对象
	 * @param obj
	 */
	free(obj: T) {
		obj.onFree();
		obj.pool = null;
		if (this.count < this.max) {
			this.objs.push(obj);
		}
	}

	/**
	 * 移除对象池里所有对象
	 */
	clear() {
		this.objs.length = 0;
	}
}


/**
 * 对象池对象
 */
export class PoolObj implements IPoolObj {

	pool: Pool<this>;

	onAlloc() { }

	onFree() { }

	/**
	 * 回收此对象到对象池
	 */
	free() {
		this.pool.free(this);
	}

}

/**
 * 引用计数对象池对象接口
 */
export interface IRefPoolObj extends IPoolObj {
	pool: RefPool<this>;
	/** 引用计数 */
	ref: number;
}

/**
 * 引用计数对象池
 */
export class RefPool<T extends IRefPoolObj> extends Pool<T> {

	alloc() {
		const obj = this.objs.pop() || (this.createArgs ? new this.cls(...this.createArgs) : new this.cls());
		obj.ref = 1;
		obj.pool = this;
		obj.onAlloc();
		return obj;
	}

	/**
	 * 回收对象
	 * 每次调用减少一次引用计数
	 * 当引用计数为0时才会真正回收
	 * @param obj
	 */
	free(obj: T) {
		if (--obj.ref <= 0) {
			super.free(obj);
		}
	}

	/**
	 * 忽略引用计数强制回收对象
	 * @param obj
	 */
	freeForce(obj: T) {
		super.free(obj);
	}
}

/**
 * 循环对象池对象
 */
export interface IRecyclePoolObj extends IPoolObj {
	pool: RecyclePool<this>;
	/** 在池中的idx */
	poolIdx: number;
}

/**
 * 循环对象池
 * 此对象池的对象不使用时一定要回收 不然会导致池里对象越来越多
 * tip:最大容量这个参数对此对象池无效
 */
export class RecyclePool<T extends IRecyclePoolObj> extends Pool<T> {
	protected _useCount: number = 0;

	/** 使用中的对象数量 */
	get useCount() {
		return this._useCount;
	}

	/**
	 * 从池里获取一个对象，没有则创建
	 */
	alloc() {
		let obj = this.objs[this._useCount];
		if (!obj) {
			obj = this.objs[this._useCount] = this.createArgs ? new this.cls(...this.createArgs) : new this.cls();
			obj.poolIdx = this._useCount;
		}
		++this._useCount;
		obj.pool = this;
		obj.onAlloc();
		return obj;
	}

	/**
	 * 回收对象
	 * @param obj
	 */
	free(obj: T) {
		if (obj.poolIdx < this._useCount) {
			obj.onFree();
			--this._useCount;
			if (this._useCount > 0) {
				//和末尾的对象交换一下 作为下次可取出的对象
				const tail = this.objs[this._useCount];
				tail.poolIdx = obj.poolIdx;
				this.objs[obj.poolIdx] = tail;
				this.objs[this._useCount] = obj;
				obj.poolIdx = this._useCount;
			}
		}
	}
}

/**
 * 循环对象池对象
 */
export class RecyclePoolObj extends PoolObj implements IRecyclePoolObj {

	poolIdx: number;
	pool: RecyclePool<this>;

}
